﻿using HighPerformanceLog.V0;
using Microsoft.SqlServer.Server;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net.Mime;
using System.Text;
using System.Threading;
using System.Threading.Tasks;


namespace HighPerformanceLog.V1
{

    /// <summary>
    /// A powerful log manager. 
    /// </summary>
    public class HWLogger
    {
        /****************** 以下是公共变量 **********************/
        /// <summary>
        /// 日志列表缓存，通过 SaveLogs() 方法写入到文件。
        /// </summary>
        public List<HWLog> logs = new List<HWLog>();

        /// <summary>
        /// 是否自动写入，默认为 true，每隔 AutoSaveInterval 秒， 若 logs 不为空则调用 SaveLogs() 一次。 
        /// </summary>
        public bool IsAutoSaveAvailable = true;

        /// <summary>
        /// 默认的日志文件为当前程序目录下的 LogFile.txt 
        /// </summary>
        public string LogFilepath = "logs.txt";

        /// <summary>
        /// 指定自动保存的时间间隔，单位为秒，默认值为1。
        /// </summary>
        public int AutoSaveInterval = 1;

        /// <summary>
        /// 最小写入数量，即当前缓冲中的日志必需大于此数量才会写入到文件中。
        /// </summary>
        public int MinimumWriteCount = 100;

        /// <summary>
        /// 输出级别，只有满足条件的日志才会输出，默认为全部6种类型。
        /// </summary>
        public LogSeverity[] LogOutputLevels = { LogSeverity.FATAL, LogSeverity.ERROR, LogSeverity.WARNING, LogSeverity.INFO, LogSeverity.DEBUG, LogSeverity.NONE };

        /// <summary>
        /// 日志中包括此字段中的内容则不写到文件中，优先级低于 Inclusives。
        /// </summary>
        public List<string> Exclusives = new List<string>();

        /// <summary>
        /// 日志中包括此字段中的内容则必需写到文件中，优先级高于 Exclusives。
        /// </summary>
        public List<string> Inclusives = new List<string>();

        /// <summary>
        /// 日志记录时，是否是大小写敏感，对Exclusives和Inclusives生效。
        /// </summary>
        public bool CaseSensitive = false;

        /// <summary>
        /// 是否继续运行，true为运行，false将结束循环。
        /// </summary>
        public bool IsRunning = true;

        /// <summary>
        /// 设置当前日志记录器是否正常工作。
        /// </summary>
        public bool Enable
        {
            get
            {
                return IsRunning;
            }
            set
            {
                IsRunning = value;
            }
        }


        /****************** 以下是静态成员变量和方法 **********************/
        public static HWLogger CurrentLogger { get; set; } = new HWLogger();

        /// <summary>
        /// 写入FATAL类日志。
        /// </summary>
        /// <param name="content">日志内容。</param>
        /// <returns></returns>
        public static void FatalLog(string content, LogCategory logType = LogCategory.None)
        {
            CurrentLogger?.AddLog(LogSeverity.FATAL, logType, content, 2);
        }

        /// <summary>
        /// 写入ERROR类日志。
        /// </summary>
        /// <param name="content">日志内容。</param>
        /// <returns></returns>
        public static void ErrorLog(string content, LogCategory logType = LogCategory.None)
        {
            CurrentLogger?.AddLog(LogSeverity.ERROR, logType, content, 2);
        }

        /// <summary>
        /// 写入WARN类日志。
        /// </summary>
        /// <param name="content">日志内容。</param>
        /// <returns></returns>
        public static void WarningLog(string content, LogCategory logType = LogCategory.None)
        {
            CurrentLogger?.AddLog(LogSeverity.WARNING, logType, content, 2);
        }

        /// <summary>
        /// 写入DEBUG类日志。
        /// </summary>
        /// <param name="content">日志内容。</param>
        /// <returns></returns>
        public static void DebugLog(string content, LogCategory logType = LogCategory.None)
        {
            CurrentLogger?.AddLog(LogSeverity.DEBUG, logType, content, 2);
        }


        /// <summary>
        /// 写入INFO类日志。
        /// </summary>
        /// <param name="content">日志内容。</param>
        /// <returns></returns>
        public static void InfoLog(string content, LogCategory logType = LogCategory.None)
        {
            CurrentLogger?.AddLog(LogSeverity.INFO, logType, content, 2);
        }

        /// <summary>
        /// 设置当前对象的存在位置，如果当前对象为空，会自动创新对象。
        /// </summary>
        /// <param name="filename"></param>
        public static void SetLogFilepath(string filename)
        {
            if (CurrentLogger == null)
                CurrentLogger = new HWLogger();
            CurrentLogger.LogFilepath = filename;
        }

        /// <summary>
        /// 将当前的日志保存到指定的文件，默认值由路径决定的。
        /// </summary>
        public static void SaveLogs()
        {
            CurrentLogger?.Save();
        }


        public static void SetLogLevels(params LogSeverity[] logLevel)
        {
            if (CurrentLogger != null)
                CurrentLogger.LogOutputLevels = logLevel;
        }


        public static void SetInclusives(params string[] inclusives)
        {
            CurrentLogger?.Inclusives.AddRange(inclusives);
        }

        public static void SetExclusives(params string[] exclusives)
        {
            CurrentLogger?.Exclusives.AddRange(exclusives);
        }

        public static void ExitLogger()
        {
            CurrentLogger?.Exit();
        }

        /****************** 以下是私有变量 **********************/
        Thread mainThread = null;
        Thread parentThread = null;

        bool exited = false;

        public HWLogger()
        {
            LogFilepath = $"{DateTime.Now:yyyy-MM-dd}.log";
            mainThread = new Thread(MainTask);
            mainThread.Start();
        }

        public HWLogger(Thread parentThread)
        {
            this.parentThread = parentThread;
            LogFilepath = $"{DateTime.Now:yyyy-MM-dd}.log";
            mainThread = new Thread(MainTask);
            mainThread.Start();
        }

        /// <summary>
        /// 默认静态构造函数，只要使用此类就会优先调用一次本函数，作用是启动一个自动保存日志的函数。
        /// </summary>
        public void Start()
        {
            IsRunning = true;
        }

        /// <summary>
        /// 停止日志写入。
        /// </summary>
        public void Stop()
        {
            IsRunning = false;
        }

        /// <summary>
        /// 调用此方法后会中止日志后台的处理线程。
        /// </summary>
        public void Exit()
        {
            exited = true;
        }

        private void MainTask()
        {
            var lastSaveTime = DateTime.Now;
            while (true)
            {
                double duration = (DateTime.Now - lastSaveTime).TotalSeconds;
                if (IsRunning && duration > AutoSaveInterval && logs.Count > MinimumWriteCount)
                {
                    Save();
                    lastSaveTime = DateTime.Now;
                }

                if (exited || parentThread?.ThreadState != System.Threading.ThreadState.Running)
                {
                    Save();
                    break;
                }

                Thread.Sleep(10);
            }
        }

        /// <summary>
        /// 将日志 logs 保存到文件 LogFile 中。
        /// </summary>
        public void Save()
        {
            var copy_logs = new List<HWLog>();
            lock (logs)
            {
                copy_logs.AddRange(logs);
                logs.Clear();
            }

            StringBuilder sb = new StringBuilder();
            var inclusives = new List<string>();
            foreach (string s in Inclusives)
                inclusives.Add(CaseSensitive ? s : s.ToLower());

            var exclusives = new List<string>();
            foreach (string s in Exclusives)
                exclusives.Add(CaseSensitive ? s : s.ToLower());
            foreach (var item in copy_logs)
            {
                var alog = item.ToString();
                bool b1 = Contains(inclusives, CaseSensitive ? alog : alog.ToLower());
                bool b2 = Contains(exclusives, CaseSensitive ? alog : alog.ToLower());
                bool b3 = LogOutputLevels.Count(lv => lv == item.Severity) > 0;
                if (b1 || (!b2 && b3))
                    sb.AppendLine(alog);
            }
            logs.Clear();

            Common.WriteTextToFile(LogFilepath, sb.ToString());
        }

        /// <summary>
        /// 用于判断一个列表中是否包括字符串。
        /// </summary>
        /// <param name="includsives"></param>
        /// <param name="log"></param>
        /// <returns></returns>
        private bool Contains(List<string> includsives, string log)
        {
            foreach (var item in includsives)
                if (log.Contains(item))
                    return true;
            return false;
        }

        public void AddLog(LogSeverity logSeverity, LogCategory logType, string content, int traceLevel = 1)
        {
            HWLog log = new HWLog() { Severity = logSeverity, Category = logType, Content = content };
            try
            {
                var trace = new StackTrace(traceLevel, true);
                StackFrame st = new StackTrace(traceLevel, true).GetFrame(0);
                log.Method = st?.GetMethod()?.DeclaringType?.FullName + "." + st?.GetMethod()?.Name;
                log.Position = st?.GetFileName() + "@" + st?.GetFileLineNumber();
            }
            catch { }
            logs.Add(log);
        }


        /// <summary>
        /// 写入FATAL类日志。
        /// </summary>
        /// <param name="content">日志内容。</param>
        /// <returns></returns>
        public void Fatal(string content, LogCategory logType = LogCategory.None)
        {
            AddLog(LogSeverity.FATAL, logType, content, 2);
        }

        /// <summary>
        /// 写入ERROR类日志。
        /// </summary>
        /// <param name="content">日志内容。</param>
        /// <returns></returns>
        public void Error(string content, LogCategory logType = LogCategory.None)
        {
            AddLog(LogSeverity.ERROR, logType, content, 2);
        }

        /// <summary>
        /// 写入WARN类日志。
        /// </summary>
        /// <param name="content">日志内容。</param>
        /// <returns></returns>
        public void Warning(string content, LogCategory logType = LogCategory.None)
        {
            AddLog(LogSeverity.WARNING, logType, content, 2);
        }

        /// <summary>
        /// 写入DEBUG类日志。
        /// </summary>
        /// <param name="content">日志内容。</param>
        /// <returns></returns>
        public void Debug(string content, LogCategory logType = LogCategory.None)
        {
            AddLog(LogSeverity.DEBUG, logType, content, 2);
        }


        /// <summary>
        /// 写入INFO类日志。
        /// </summary>
        /// <param name="content">日志内容。</param>
        /// <returns></returns>
        public void Info(string content, LogCategory logType = LogCategory.None)
        {
            AddLog(LogSeverity.INFO, logType, content, 2);
        }


        /// <summary>
        /// 只有满足条件时才会写日志。
        /// </summary>
        /// <param name="loginfo">日志信息。</param>
        /// <param name="condition">条件, true表示满足。</param>
        /// <param name="lv"></param>
        public void LogIf(string loginfo, bool condition, LogSeverity lv = LogSeverity.INFO)
        {

        }
    }


}

